#!/usr/bin/env node
const path = require('path')
const {Readable} = require('stream')
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc')

const app = new Koa()

// 将文件流转字符串
const streamToStr = stream => new Promise((resolve, reject) => {
    const chunks = []
    // 不同事件的处理
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
    stream.on('error', reject)
})

const stringToSteam = text => {
    const stream = new Readable()
    stream.push(text)
    stream.push(null)
    return stream
}

// 3. 加载第三方模块
app.use(async (ctx, next) => {
    // ctx.path --> /@modules/vue
    if (ctx.path.startsWith('/@modules/')) {
        const moduleName = ctx.path.substr(10)
        const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
        const pkg = require(pkgPath)
        // pkg.module -> 入口文件
        ctx.path = path.join('/node_modules', moduleName, pkg.module)
    }
    await next()
})

// 1. 开启静态文件服务器
app.use(async (ctx, next) => {
    await send(ctx, ctx.path, {root: process.cwd(), index: 'index.html'})
    await next()
})

// 4. 处理单文件组件
app.use(async (ctx, next) => {
    if (ctx.path.endsWith('.vue')) {
        const contents = await streamToStr(ctx.body)
        // 可以通过descriptor获取编译后的js代码
        const {descriptor} = compilerSFC.parse(contents);
        let code
        // 第一次请求
        if (!ctx.query.type) {
            // vue2是script,vue3是scriptSetup
            if (descriptor.script) {
                code = descriptor.script.content
                code = code.replace(/export\s+default\s+/g, 'const __script = ')
            }
            // 不是很懂怎么搞setup
            // else {
            //     code = descriptor.scriptSetup.content
            //     code = code.replace(/import\s*(\w+)\s*from\s*['"]([^'"\/.]*)['"]/g, `
            //         const __script =
            //     `)
            // }

            code += `
                    import { render as __render } from "${ctx.path}?type=template"
                    __script.render = __render
                    export default __script
                `
        }
        // 第二次请求
        else if (ctx.query.type === 'template') {
            // 编译模板
            const templateRender =
                compilerSFC.compileTemplate({source: descriptor.template.content})
            code = templateRender.code
        }
        ctx.type = 'application/javascript'
        ctx.body = stringToSteam(code)
    }
    await next()
})

// 返回静态文件时,先判断是不是第三方模块,如果是,就返回模块路径
// 2. 修改第三方模块的路径
app.use(async (ctx, next) => {
    if (ctx.type === 'application/javascript') {
        const contents = await streamToStr(ctx.body)
        // import vue from 'vue' 第三方模块需要改为@module/xxx
        // import app from './app.vue' 相对路径不需要处理,可以正常引入
        ctx.body = contents
            .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
            // 替换process.env.NODE_ENV
            .replace(/process\.env\.NODE_ENV/g, '"development"')
    }
    await next()
})

app.listen(5173)
console.log('Server running @ http://localhost:5173')